﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Threading;
using TimeToGo.Formatters;
using TimeToGo.Presentation.Views;
using System.ComponentModel;
using TimeToGo.Services;
using TimeToGo.Infrastructure;
using TimeToGo.Repositories;

namespace TimeToGo.Presentation.ViewModels
{
    internal class MainViewModel : ViewModelBase
    {
        public ObservableCollection<TimeInfoDecorator> Times { get; set; }
        public ObservableCollection<DurationFormatterProxy> DurationFormatters { get; set; }

        public SimpleDelegateCommand<object> EditTimesCommand { get; set; }
        public SimpleDelegateCommand<string> SelectFormatCommand { get; set; }
        public SimpleDelegateCommand<string> SelectTimeCommand { get; set; }

        private readonly ITimeSource _timeSource;
        private readonly Dispatcher _uiDispatcher;
        private readonly IRepository<TimeInfo> _timeRepository;
        private DurationFormatterProxy _formatter;

        private string _windowTitle;
        public string WindowTitle
        {
            get { return _windowTitle; }
            set
            {
                if (_windowTitle != value)
                {
                    _windowTitle = value;
                    OnPropertyChanged("WindowTitle");
                }
            }
        }

        private string _displayText;
        public string DisplayText
        {
            get { return _displayText; }
            set 
            {
                if (_displayText != value)
                {
                    _displayText = value;
                    OnPropertyChanged("DisplayText");
                }
            }
        }

        public MainViewModel(IRepository<TimeInfo> timeRepository)
        {
            _timeRepository = timeRepository;
            _uiDispatcher = Dispatcher.CurrentDispatcher;

            _timeSource = new TimeSource();
            _timeSource.TimeElapsed += _timeSource_TimeElapsed;

            Times = new ObservableCollection<TimeInfoDecorator>();
            DurationFormatters = GetDUrationFormatters();

            EditTimesCommand = new SimpleDelegateCommand<object>(EditTimes);
            SelectFormatCommand = new SimpleDelegateCommand<string>(SelectFormat);
            SelectTimeCommand = new SimpleDelegateCommand<string>(SelectTime);

            if (DurationFormatters.Count > 0)
                SelectFormat(DurationFormatters[0].Name);

            LoadTimes();
        }

        private static ObservableCollection<DurationFormatterProxy> GetDUrationFormatters()
        {
            // format string {0:0,0} means apply thousand separaters to the string
            return new ObservableCollection<DurationFormatterProxy>
                             {
                                 new DurationFormatterProxy(new SecondDurationFormatter("{0:0,0\nseconds}")),
                                 new DurationFormatterProxy(new DayDurationFormatter("{0:#,#.##\ndays}")),
                                 new DurationFormatterProxy(new BinaryDurationFormatter("{0} bits\n")),
                                 new DurationFormatterProxy(new TimeDurationFormatter("{0}d {1}h {2}m {3}s\n"))
                             };
        }

        private void LoadTimes()
        {
            // remember then clear the last one if there was one selected...
            string lastSelected = null;
            if (_timeSource.CurrentTime != null)
            {
                lastSelected = _timeSource.CurrentTime.Name;
                _timeSource.CurrentTime = null;
            }

            // clear the list and reload them all...
            Times.Clear();
            _timeRepository.FindAll().ToList().ForEach(t => Times.Add(new TimeInfoDecorator { Name = t.Name, Time = t.Time }));

            // try and select the previous one...
            SelectTime(lastSelected);
            // if its not there anymore, select the first one...
            if (_timeSource.CurrentTime == null && Times.Count > 0)
                SelectTime(Times[0].Name);
        }

        private void _timeSource_TimeElapsed(object sender, TimeElapsedEventArgs e)
        {
            Action action = () => DisplayText = _formatter.GetDuration(e.Duration);
            _uiDispatcher.Invoke(action);
        }

        private void EditTimes(object parameter)
        {
            IView timesView = new TimesVeiw();
            timesView.ShowDialog();
            LoadTimes();
        }

        private void SelectFormat(string parameter)
        {
            if (string.IsNullOrEmpty(parameter)) 
                return;

            DurationFormatterProxy formatter = (from f in DurationFormatters
                                                where f.Name == parameter
                                                select f).FirstOrDefault();
            if (formatter != null)
            {
                if (_formatter != null)
                    _formatter.Selected = false;

                formatter.Selected = true;
                _formatter = formatter;
            }
        }

        private void SelectTime(string parameter)
        {
            if (string.IsNullOrEmpty(parameter))
                return;

            TimeInfoDecorator selectedTime = (from ti in Times
                                              where ti.Name == parameter
                                              select ti).FirstOrDefault();
            if (selectedTime != null)
            {
                if (_timeSource.CurrentTime != null)
                    _timeSource.CurrentTime.Selected = false;

                selectedTime.Selected = true;
                _timeSource.CurrentTime = selectedTime;
                WindowTitle = "Time To Go: " + selectedTime.Name;
            }
        }
    }
}